package org.remixlab.gnutopic.client;

import java.awt.Color;

import java.text.SimpleDateFormat;
import java.util.ArrayList;


import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;

import org.remixlab.gnutopic.data.CSVDataManager;
import org.remixlab.gnutopic.data.XmlParser;
import org.remixlab.gnutopic.pojo.ForumCategory;
import org.remixlab.gnutopic.pojo.ForumMessage;
import org.remixlab.gnutopic.pojo.ForumThread;

import controlP5.ControlEvent;
import controlP5.ControlP5;
import controlP5.ControllerGroup;
import controlP5.ListBox;
import controlP5.Range;
import controlP5.Slider;
import controlP5.Textfield;

import codeanticode.glgraphics.GLConstants;
import codeanticode.glgraphics.GLGraphics;
import codeanticode.glgraphics.GLModel;

import processing.core.PApplet;
import processing.core.PGraphics;
import processing.core.PVector;
import processing.core.PFont;
import remixlab.proscene.AxisPlaneConstraint;
import remixlab.proscene.CameraConstraint;
import remixlab.proscene.Scene;
import remixlab.proscene.WorldConstraint;

/**
 * Primer experimento para probar revisar la visualización dentro de un foro.
 * @author mauro
 *
 */

public class SpiralVisualization  extends PApplet{

	private SpiralPainter painter; 	//se encarga de dibujar el hilo...
	
	//ancho y alto de la aplicación
	private int width = 800;
	private int height = 600;
	
	//color de fondo
	private int background = 255;
	
	private int backgroundThread = 80;
	
	//color de la fuente
	private int fontColor = 0;
	
	//de proscene
	private Scene scene;
	
	private GLModel spiralModel;
	
	private GLModel threadModel;

	//arbol donde se almacenarán todos los hilos del foro.
	public TreeMap<Date, ForumThread> threads;
	
	public Collection<ForumThread> collectionThread;
	
	//lista de las categorías del foro.
	public ArrayList<ForumCategory> categories;
	
	public ArrayList<GLModel> categoryModels;
	
	public boolean animate = true;
	
	public boolean updateCoords = false;
	
	public boolean updateColors = false;
	
	public boolean filter = false;
	
	public Date filterDate = null;
	
	public GLGraphics renderer; 
	
	public ControlP5 controlP5;
	
	//variables para manejar la camara.
	public GNUTopicCP cpProfile;
	
	AxisPlaneConstraint constraints[] = new AxisPlaneConstraint[2];
	
	int activeConstraint = 1;
	
	public PVector zWorld = new PVector(0, 0, 1);
	
	public PVector zWorldCam;
	
	//variables para aplicar los filtros.
	public int categoryId = -1;
	
	public String categoryName = "All";
	
	public SimpleDateFormat dateFormat;
	
	//campos necesarios para el filtro de fechas.
	//fecha mínima del thread
	public Date minDate = null;
	
	//fecha del último thread
	public Date maxDate = null;
	
	//pgraphic encargado de dibujar el hilo...
	public PGraphics pgraphic = null;
	
	//coordenada x,y donde se dibujará el pgraphic
	public int pgraphicX = 25;
	public int pgraphicY = 25;
	
	public int pgraphicWidth = 750;
	public int pgraphicHeight = 550;
	
	//indica si hay un hilo seleccionado...
	boolean selected = false;
	
	//almacena el hilo seleccionado a visualizar...
	ForumThread selectedThread = null;
	
	CircleButton closeButton;
	
	public Map<String, ArrayList<Integer>> searchResult;
	
	public String text = "";
	
	public void animateScene(Scene s){
		if(animate){
			//System.out.println("animate...");
			painter.updateLines(categories, updateCoords, updateColors);
			
			animate = painter.updateDots(categories, updateCoords, updateColors);
			
			if(!animate){
				//inicializo las variables otra vez
				updateCoords = false;
				updateColors = false;
			}
		}
	}
	
	public int getFontColor() {
		return fontColor;
	}

	public void setFontColor(int fontColor) {
		this.fontColor = fontColor;
	}

	public GLModel getThreadModel() {
		return threadModel;
	}

	public void setThreadModel(GLModel threadModel) {
		this.threadModel = threadModel;
	}
	
	public Map<String, ArrayList<Integer>> getSearchResult() {
		return searchResult;
	}

	public void setSearchResult(Map<String, ArrayList<Integer>> searchResult) {
		this.searchResult = searchResult;
	}

	/**
	 * Implementación del método setup()
	 */
	public void setup(){
		//inicializa el tamaño del applet
		size(width,height,GLConstants.GLGRAPHICS);
		
		scene = new Scene(this);
		
		//scene.setShortcut('f', Scene.KeyboardAction.DRAW_FRAME_SELECTION_HINT);
		scene.setAxisIsDrawn(false);
		scene.setGridIsDrawn(false);
		scene.enableKeyboardHandling(false);
		scene.setCameraType(remixlab.proscene.Camera.Type.ORTHOGRAPHIC);
		scene.setRadius(5000);
		scene.setCenter(new PVector(0, 0, 1000));
		scene.showAll();
		
		//agrego la camara personalizada...
		cpProfile = new GNUTopicCP(scene, "MY_PROFILE");
		scene.registerCameraProfile(cpProfile);
		
		//des registro las otras camaras...
		scene.unregisterCameraProfile("FIRST_PERSON");
		scene.unregisterCameraProfile("WHEELED_ARCBALL");
	
		//creo el contraint 1
		constraints[0] = new WorldConstraint();
		
		//Rotación
		constraints[0].setRotationConstraintType(AxisPlaneConstraint.Type.AXIS);
		constraints[0].setRotationConstraintDirection(new PVector(0, 0, 1));
		
		//creo el contraint 2
		constraints[1] = new CameraConstraint(scene.camera());
		constraints[1].setRotationConstraintType(AxisPlaneConstraint.Type.AXIS);
		constraints[1].setRotationConstraintDirection(new PVector(1, 0, 0));
		
		//agrego el constraint al scene
		scene.camera().frame().setSpinningSensitivity(100);
		scene.camera().frame().setConstraint(constraints[activeConstraint]);
		
		//obtengo el renderer...
		renderer = (GLGraphics) this.g;
		
		scene.addAnimationHandler(this, "animateScene");
		
		//scene.background(background);
		
		stroke(0);
		
		painter = new SpiralPainter();
		
		//aqui, hago referencia al padre.
		painter.setPapplet(this);
		
		painter.setWidth(width);
		painter.setHeight(height);
		
		//obtengo las categorias y sub categorias del foro
		//categories = CSVDataManager.getInstance().getCategories();
		XmlParser.getInstance().setPath(this.dataPath("")+"/forum_processing.xml");
		
		XmlParser.getInstance().parseXmlFile();
		
		categories = XmlParser.getInstance().getCategories();
		
		//obtengo los threads del foro
		threads = XmlParser.getInstance().getThreads();
		
		minDate = threads.firstKey();
		painter.setMinThreadDate(minDate);
		
		maxDate = threads.floorKey(new Date());
		painter.setMaxThreadDate(maxDate);
		
		//creo el date format
		dateFormat = new SimpleDateFormat("dd/MM/yyyy");
		
		//aqui inicializo la gui
		initGUI();
		
		//inicializo el collectionThreads
		collectionThread = threads.values();
		
		//envío a dibujar los threads...
		categoryModels = painter.initDrawCategories(categories);
		
		scene.startAnimation();
		
		//aqui creo el pgraphic
		pgraphic = this.createGraphics(pgraphicWidth, pgraphicHeight, this.JAVA2D);
		
		pgraphic.smooth();
		
		//cargo la fuente usada...
		//fontA = loadFont("Ziggurat-HTF-Black-32.vlw");
		//textFont(fontA, 24);
		
		//inicializo el botón de cerrar...
		closeButton = new CircleButton(pgraphicWidth - 10, 10, 10);
		
		//agrego el pgraphic.
		closeButton.setParent(pgraphic);
		
	}
	
	public void initGUI(){
		//creo el controlP5
		controlP5 = new ControlP5(this);
		controlP5.setAutoDraw(false);
		
		//creo la lista para filtrar por categorías...
		ListBox categoryListBox = controlP5.addListBox("categoryList", 50,50,150,80);
		
		categoryListBox.setItemHeight(15);
		categoryListBox.setBarHeight(15);
		
		categoryListBox.captionLabel().toUpperCase(true);
		categoryListBox.captionLabel().set("categories");
		categoryListBox.captionLabel().style().marginTop = 3;
		categoryListBox.valueLabel().style().marginTop = 3;
		
		/*categoryListBox.setColorBackground(color(255,128));
		categoryListBox.setColorActive(color(0,0,255,128));*/
		
		//ahora, recorro cada una de las categorías y las agrego a la lista.
		categoryListBox.addItem("all", -1);
		
		if(categories!=null && categories.size()>0){
			for(ForumCategory category : categories){
				//agrego la categoría a la lista.
				categoryListBox.addItem(category.getName(), category.getId());
				
				//ahora obtengo las sub categorías y las filtro...
				ArrayList<ForumCategory> subCategories = category.getSubCategories();
				
				if(subCategories!=null && subCategories.size()>0){
					for (ForumCategory subCategory : subCategories){
						categoryListBox.addItem("-"+subCategory.getName(), subCategory.getId());
					}
				}
			}
		}
		
		//se agrega el control para la fecha
		controlP5.addRange("dateRange", minDate.getTime(), maxDate.getTime(), minDate.getTime(), maxDate.getTime(), 50,150, 150, 12);
		
		controlP5.addTextfield("search", 50,220,150,15);
		
	}
	
	
	/**
	 * Implementación del método draw()
	 */
	public void draw(){
		
		this.background(background);
		
		// limite la navegacion en z:
		PVector pos = scene.camera().position();
		if (pos.z <= 0) {
			if (mouseY <= pmouseY){
				scene.disableMouseHandling();
			}else{
				scene.enableMouseHandling();
			}
		}
		
		// limite la navegacion para que la escena no quede "patas arriba"
		zWorldCam = scene.camera().frame().transformOf(zWorld);  
		/*if (zWorldCam.y > 0) {
			if (mouseY >= pmouseY){
				scene.disableMouseHandling();
			}else{
				scene.enableMouseHandling();
			}
		}*/
		
		renderer.beginGL();
		
		if(spiralModel!=null){
			//obtengo el modelo de espiral y le hago el render..
			renderer.model(spiralModel);
		}
		
		if(categoryModels!=null && categoryModels.size()>0){
			//para que se vea la transparencia.
			renderer.setDepthMask(false);
			
			for(GLModel categoryModel : categoryModels){
				//dibujo todos los hilos...
				renderer.model(categoryModel);	
			}
			
			renderer.setDepthMask(true);
		}
		
		renderer.endGL();
		
		if(collectionThread!=null){
			Iterator<ForumThread> iterator = collectionThread.iterator();
			
			while(iterator.hasNext()){
				ForumThread thread = iterator.next();
				
				thread.draw(false);
			}
		}
		
		//aqui verifico si hay que filtrar o no..
		if(filter){
			if(filterDate!=null){
				//obtengo la fecha actual..
				Date currentDate = new Date();
				
				//aqui verifico que la diferencia entre la fecha actual y la 
				//fecha en que se filtro sea mayor a 2 sg
				if(currentDate.getTime()-filterDate.getTime()>1000){
					
					filterThreads();
					filterDate = new Date();
					
					updateColors = true;
					animate=true;
					filter = false;
				}
				
			}else{
				
				filterThreads();
				
				filterDate = new Date();
				
				updateColors = true;
				animate=true;
				filter = false;
			}
			
		}
		
		saveState();
		
		if(selected){
			if(selectedThread!=null){
				//se des habilita el mouse de proscene...
				scene.disableMouseHandling();
				
				//dibujo el hilo
				drawThread();	
			}else{
				//si no hay un hilo seleccionado.. no tengo nada que mostrar,
				//por lo que cambio la variable a false.
				selected = false;
			}
		}else{
			//habilito el mouse
			scene.enableMouseHandling();
			
			//dibujo la interfaz gráfica...
			gui();
			
			fill(fontColor);
			//aqui muestro el texto de las categorías
			text("Categories: "+ categoryName, 50, 180);
			
			text("From "+dateFormat.format(minDate)+" to "+dateFormat.format(maxDate), 50, 200);
			
			text("Text: "+text, 50, 270);
		}
		
		restoreState();
		
	}
	
	/**
	 * Dibuja el thread con mayor detalle, para poder ver la información contenida en el mismo..
	 */
	public void drawThread(){
		//System.out.println("dibujando el hilo: "+selectedThread.toString());
		
		//aqui muestro el el pgraphics..
		pgraphic.beginDraw();
		
		pgraphic.background(backgroundThread, backgroundThread, backgroundThread, 80);
		
		if(closeButton!=null){
			closeButton.overCircle(mouseX-pgraphicX, mouseY-pgraphicY);
			
			closeButton.draw();
		}
		
		//si el hilo es diferente de nulo...
		if(selectedThread!=null){
			//obtengo los mensajes a dibujar...
			ArrayList<ForumMessage> messages = selectedThread.getMessages();
			
			//creo un hashMap para almacenar los usuarios que intervienen en el mensaje.
			Map<String, Color> userColor = new HashMap<String, Color>();
			
			Map<String, Color> userSelectColor = new HashMap<String, Color>();
			
			String userName = "";
			float sat = 0.75f;
			float hue = 0f;
			
			
			int i = 0;
			
			int messageWidth = 20;
			int messageHeight = 20;
			
			if(messages.size()>0){
				int messageSpace = (int) pgraphicHeight/messages.size();
				//System.out.println("messagesNumber: "+messages.size()+" spice: "+messageSpace);
				
				//aqui reviso si el hilo esta en el hashMap o no.
				ArrayList<Integer> selectedMessages = searchResult.get(selectedThread.getId());
				
				for(ForumMessage message : messages){
					
					CircleButton circleMessage = message.getCircle(); 
					
					//si el circulo es nulo para el mensaje, debo crearlo..
					if(circleMessage==null){
						//creo el circulo...
						circleMessage = new CircleButton(20, (i*messageSpace)+(i+1*(messageWidth/2))+10, messageWidth);
						
						//asigno el padre
						circleMessage.setParent(pgraphic);
						
						//selecciono el color, dependiendo del usuario.
						
						//verifico que el usuario no este en el hash map
						userName = message.getUserName();
						
						//obtengo el color que representa a este usuario...
						Color color = userColor.get(userName);
						
						Color selectColor = userSelectColor.get(userName);
						
						//si es diferente de null, significa que el usuario ya ha 
						//publicado en el mensaje en el thread antes...
						if(color==null || selectColor==null){
							//si el nulo, significa que hasta este momento no ha publicado
							//ningún mensaje, entonces debo crear un color y asignarselo..
							
							//TODO: por ahora lo dejo todo del mismo color (color rojo...)
							int rgb= Color.HSBtoRGB(hue, sat, 0.8f);
							
							color = new Color(rgb);
							
							rgb= Color.HSBtoRGB(hue, sat, 1f);
							
							selectColor = new Color(rgb);
							
							//agrego los colores al hashMap
							userColor.put(userName, color);
							userSelectColor.put(userName, selectColor);
							
							//incremento el hue
							hue = hue + 0.06f;
						}
						
						circleMessage.setColor(color);
						circleMessage.setSelectedColor(selectColor);
						
						//asigno el circulo al mensaje.
						message.setCircle(circleMessage);
						
						i++;
					}
					
					//reviso si el mouse esta sobre el botón...
					boolean messageSelect = circleMessage.overCircle(mouseX-pgraphicX, mouseY-pgraphicY);
					
					//si esta seleccionado, muestro la información del mensaje...
					if(messageSelect){
						
						pgraphic.fill(fontColor);
						pgraphic.text(message.toString(), 50, 50, pgraphicWidth - 100, pgraphicHeight - 100);
					}
					
					//aqui ahora dibujo el mensaje.
					circleMessage.draw();
				}
			}
		}
		
		pgraphic.endDraw();
		this.image(pgraphic, pgraphicX, pgraphicY);
		//finalizo el pgraphics
		
	}
	
	public void gui() {
		controlP5.draw();
	}

	public void saveState() {
		// 1. Disable depth test to draw 2d on top of 3d. Don't know why 
		// it's not working properly, uncomment to see:
		scene.pg3d.hint(DISABLE_DEPTH_TEST);
		// 2. Set processing projection and modelview matrices to draw in 2D:
		// more or less inspire from here:
		// http://www.opengl.org/wiki/Viewing_and_Transformations#How_do_I_draw_2D_controls_over_my_3D_rendering.3F
		// 2.a projection matrix:
		float cameraZ = ((height/2.0f) / tan(PI*60.0f/360.0f));
		scene.pg3d.perspective(PI/3.0f, scene.camera().aspectRatio(), cameraZ/10.0f, cameraZ*10.0f);
		// 2.b model view matrix
		scene.pg3d.camera();
	}
	
	public void restoreState() {
		// 1. Restore processing projection matrix
		switch (scene.camera().type()) {
			case PERSPECTIVE:
				scene.pg3d.perspective(scene.camera().fieldOfView(), scene.camera().aspectRatio(), scene.camera().zNear(), scene.camera().zFar());
			break;
			case ORTHOGRAPHIC:
				float[] wh = scene.camera().getOrthoWidthHeight();
				scene.pg3d.ortho(-wh[0], wh[0], -wh[1], wh[1], scene.camera().zNear(), scene.camera().zFar());
			break;
		}
		// 2. Restore processing modelview matrix
		scene.pg3d.camera(scene.camera().position().x, scene.camera().position().y, scene.camera().position().z, 
				scene.camera().at().x, scene.camera().at().y, scene.camera().at().z, 
				scene.camera().upVector().x, scene.camera().upVector().y, scene.camera().upVector().z);
		
		// 3. Re-enable the depth test, but it's not working, (see note above in saveState)
		scene.pg3d.hint(ENABLE_DEPTH_TEST);
	}
	
	public GLModel getSpiralModel(){
		return spiralModel;
	}
	
	public void setSpiralModel(GLModel model){
		this.spiralModel = model;
	}

	public Scene getScene() {
		return scene;
	}

	public void setScene(Scene scene) {
		this.scene = scene;
	}
	
	public boolean isSelected() {
		return selected;
	}

	public void setSelected(boolean selected) {
		this.selected = selected;
	}
	
	public ForumThread getSelectedThread() {
		return selectedThread;
	}

	public void setSelectedThread(ForumThread selectedThread) {
		this.selectedThread = selectedThread;
	}

	public void keyPressed(){
		
		Textfield textField = (Textfield) controlP5.controller("search");
		
		if(!textField.isFocus()){
		
			if(key=='v' || key=='V'){
				System.out.println(key);
				
				//incremento las horas...
				float h = painter.getH();
				
				if(h>1){
					//cambio el tamaño de h
					h=h-1;
				}else{
					h=24;
				}
				
				System.out.println("h= "+h);
				//cambio la hora..
				painter.setH(h);
				
				float r = (float) (h*0.0001)/6;
				
				System.out.println("r= "+r);
				
				painter.setGradeConstant(r);
				
				updateCoords = true;
				
				animate=true;
			}
			if(key == 'c' || key == 'C'){
				//println(frameRate);
				
				System.out.println(key);
				
				//incremento las horas...
				float h = painter.getH();
				
				if(h<72){
					//cambio el tamaño de h
					h=h+1;
				}else{
					h=1;
				}
				
				System.out.println("h= "+h);
				//cambio la hora..
				painter.setH(h);
				
				float r = (float) (h*0.0001)/6;
				
				System.out.println("r= "+r);
				
				painter.setGradeConstant(r);
				
				updateCoords = true;
				
				animate=true;
			}
			
			/*if(key == 'b'){
				//aumentamos la velocidad en 2 unidades..
				float v = painter.getVelocity();
				v = v+2;
				painter.setVelocity(v);
			}*/
			
			if(key == 'b' || key == 'B'){
				//cambia la visualización..
				int vis = painter.getVisualizationNumber();
				
				if(vis==1){
					vis=2;
				}else{
					vis=1;
				}
				
				painter.setVisualizationNumber(vis);
				
				updateCoords = true;
				
				animate=true;
			}
			
			if (key == 'x' || key == 'X') {
				if (activeConstraint == 0){
					activeConstraint = 1;
				}else{
					activeConstraint = 0;
				}
				scene.camera().frame().setConstraint(constraints[activeConstraint]);
			}
			/*if (key == 'y' || key == 'Y') {
			    activeConstraint = 1;
			    scene.camera().frame().setConstraint(constraints[activeConstraint]);
			    Camera cam = scene.camera().clone();
			    cam.setPosition(new PVector(0, 0, 1));
			    cam.lookAt(scene.center());
			    cam.showEntireScene();
			    scene.camera().interpolateTo(cam.frame(), 1);
			}
			if(key == 'n' || key=='N'){
				filterThreadsByCategory("Contribution_Physical");
				animate=true;
			}
			
			if(key == 'm' || key=='M'){
				filterThreadsByCategory("all");
				animate=true;
			}*/
			
		}
	}
	
	public void mousePressed(){
		System.out.println("mouse Pressed...");
		if(selected){
			//si es selected, significa que seleccionó el botón de cerrar..
			//entonces debo cerrar el pgraphic..
			if(closeButton!=null && closeButton.isSelected()){
				selected = false;
				System.out.println("selected = "+selected);
				
				//borro el hilo seleccionado
				selectedThread = null;
			}
		}
		
	}
	
	public boolean isFilter() {
		return filter;
	}

	public void setFilter(boolean filter) {
		this.filter = filter;
	}

	/**
	 * maneja los eventos de la interfaz gráfica
	 * @param theEvent
	 */
	public void controlEvent(ControlEvent theEvent) { 
		if (theEvent.isGroup()) {
		    String listBoxName = theEvent.group().name();
		    
		    if(listBoxName!=null){
		    	if(listBoxName.equalsIgnoreCase("categoryList")){
			    	categoryId = (int)theEvent.group().value();
			    	
			    	//obtengo el nombre de la categoría y lo actualizo en el 
			    	//atributo categoryName...
			    	
			    	if(categoryId>0){
			    		for (ForumCategory category : categories){
				    		//pregunto si la categoría es esta...
				    		if(category.getId()==categoryId){
				    			//si son iguales, entonces... cambio el nombre..
				    			categoryName = category.getName();
				    			
				    		}else{
				    			//si es falso, debo buscar en las sub categorías.
				    			ArrayList<ForumCategory> subCategories = category.getSubCategories();
				    			
				    			for(ForumCategory subCategory : subCategories){
				    				
				    				if(subCategory.getId()==categoryId){
				    					categoryName = subCategory.getName();
				    				}
				    			}
				    		}
				    	}
			    	}else{
			    		categoryName = "All";
			    	}
			    	
			    	
			    	
			    	
			    }
		    	
		    	filter = true;
		    }
			
		  }else if(theEvent.isController()){
			  String controllerName = theEvent.controller().name();
			  
			  if(controllerName!=null){
				  if(controllerName.equalsIgnoreCase("dateRange")){
					  
					  Range temp = (Range) theEvent.controller();
					  
					  if(temp!=null){
						
						  minDate = new Date((long) temp.arrayValue()[0]);
						  maxDate = new Date((long) temp.arrayValue()[1]);
						  
						  filter = true;
					  }
				  }else if(controllerName.equalsIgnoreCase("search")){
					  
					  Textfield textField = (Textfield) theEvent.controller();
					  
					  text = textField.getText();
					  
					  if(!text.equalsIgnoreCase("")){
						  SearcherThread searcher = new SearcherThread();
						  
						  searcher.setApplet(this);
						  searcher.setQuery(text);
						  
						  searcher.run();  
					  }
					  
				  }
			}
		}
	}
	
	
	/**
	 * Método que se encarga de filtrar los hilos por categoría y por fecha
	 */
	public void filterThreads(){
		//recorro las categorías....
		for(int i = 0; i<categories.size();i++){
			
			//obtengo la categoría...
			ForumCategory category = categories.get(i);
			
			TreeMap<Date, ForumThread> threads = category.getThreadsByDate();
			
			if(threads!=null){
				//recorro uno a uno los threads de esta categoría....
				Collection<ForumThread> collectionThread = threads.values();
				
				Iterator<ForumThread> iterator = collectionThread.iterator();
				
				while(iterator.hasNext()){
					
					ForumThread thread = iterator.next();
					
					if(!(categoryId<=0)){
						if(categoryId%100!=0){
							//obtengo la categoria a la que pertenece el hilo.
							ForumCategory threadCategory = thread.getCategory();
							
							int threadCategoryId = 0;
							
							if(threadCategory!=null){
								//si el nombre es igual al recibido.. entonces.. dejo visible el hilo
								//de lo contrario lo oculto.
								threadCategoryId = threadCategory.getId();
								if(threadCategoryId==categoryId){
									thread.setVisibled(true);
								}else{
									thread.setVisibled(false);
								}
							}else{
								System.out.println("error, category is null");
							}
						}else{
							
							//obtengo la categoria a la que pertenece el hilo.
							ForumCategory threadCategory = thread.getCategory();
							
							if(threadCategory!=null){
								
								//ahora obtengo el padre de la categoría
								ForumCategory threadCategoryParent = threadCategory.getParentCategory();
								
								if(threadCategoryParent!=null){
									int threadCategoryId = 0;
									
									//si el nombre es igual al recibido.. entonces.. dejo visible el hilo
									//de lo contrario lo oculto.
									threadCategoryId = threadCategoryParent.getId();
									if(threadCategoryId==categoryId){
										thread.setVisibled(true);
									}else{
										thread.setVisibled(false);
									}	
								}else{
									System.out.println("error, parent category is null");
								}
							}else{
								System.out.println("error, category is null");
							}
						}
					}else{
						thread.setVisibled(true);
					}
					
					//aqui filtro por fecha...
					Date threadDate = thread.getDate();
					
					if(!(threadDate.getTime()>=minDate.getTime() &&
						threadDate.getTime()<=maxDate.getTime())){
						
						thread.setVisibled(false);
					}
					
					//aqui filtro por el hilo de busqueda...
					if(searchResult!=null && searchResult.size()>0){
						//si es diferente, entonces quiere decir que si existe.
						if(searchResult.get(thread.getId())==null){
							thread.setVisibled(false);
						}
					}
					
				}
			}else{
				System.out.println("error, threads is null");
			}
		}	
	}
	
	public static void main (String [] args){
		PApplet.main(new String [] {"--present", "org.remixlab.gnutopic.client.SpiralVisualization"});
	}
}
